import { VNode, createVNode } from "vue";
import { FormItem } from "../basic-form-box/form-item";
import { ButtonItemOptions, FormBox, FormItemOptions } from "../basic-form-box/type";
import { Page } from "../page";
import DataQueryLayoutVue from "./data-query-layout.vue";
import {
	LoadChildTableOptions,
	TableBox,
	TableColsOptions,
	TableOperateType,
} from "../basic-table-box/type";
import { BasicFormBox } from "../basic-form-box/basic-form-box";
import { BasicTaleBox } from "../basic-table-box";
import { HeadersType, HttpRequest, HttpResult, Methods } from "@/http";
import { ButtonItem } from "../basic-form-box/button-item";
import { repaintProxy, signReplace } from "../utils";
import { FORM_BOX_METHOD, TABLE_BOX_METHOD } from "../config";
import { FormItemComponentType } from "../basic-form-box";

interface IResult<T> {
	// 页数
	page: number;
	// 条数
	pageSize: number;
	// 总条数
	total: number;
	[x: string]: any;
	items: T;
}
type QueryPageRequestOptions<F = Record<string, any>, T = any> = {
	/** 请求头 */
	headerTyps?: HeadersType;
	/** 远程数据存储字段 */
	resultField?: "items" | "data" | string;
	/** 请求方式 */
	method?: Methods;
	/** 当 filterSearchData 存在时, 是否将表单参数合并至 filterSearchData 结果中 默认合并 */
	mergeParams?: boolean;
	/** 过滤请求参数 */
	searchFilter?: (formVlaue: F) => Record<string, any>;
	/** 请求数据二次处理方法 */
	filterData?: (dataArray: any[]) => T[];
	/** 当请求地址不止存在分页信息时可以通过该方法的返回值进行配置 */
	otherCodeReplace?: (formVlaue: F) => Record<string, any>;
};

export abstract class DataQueryPage<Form, Table>
	extends Page
	implements FormBox<Form>, TableBox<Table>
{
	// 表单实例
	private formInstance: BasicFormBox<Form>;
	// 表格实例
	private tableInstance: BasicTaleBox<Table>;
	private option: Partial<QueryPageRequestOptions<Form, Table>>;
	name: string = "";
	private url: string = "";
	private pageInfo = {
		pageSize: 20,
		page: 1,
	};
	// 布局器实例
	/**
	 * 页面构造器
	 * @param name 页面的路由名称
	 * @param url 请求的地址 #pageSize# #pageber# 进行页面请求分页的字段替换 示例： /page/#page#/#pageSize#/classfiy
	 */
	constructor(
		name: string,
		url?: string,
		option: Partial<QueryPageRequestOptions<Form, Table>> = {}
	) {
		super(name);
		this.option = option;
		if (url) {
			this.url = url;
		}
		// 初始化
		if (name) {
			this.name = name;
		} else {
			throw new Error(`请设置页面的名字:name,当前的name值为：${name}`);
		}
		this.formInstance = repaintProxy(
			new BasicFormBox({
				options: [],
			}),
			key => {
				return FORM_BOX_METHOD.includes(key);
			},
			() => {
				this.repaint();
			}
		);
		this.tableInstance = repaintProxy(
			new BasicTaleBox({
				changePage: page => {
					this.pageInfo.page = page.page;
					this.pageInfo.pageSize = page.pageSize;
					if (this.url) {
						this.requestPageData(this.pageInfo.page, this.pageInfo.pageSize);
					}
				},
			}),
			key => {
				return TABLE_BOX_METHOD.includes(key);
			},
			() => {
				this.repaint();
			}
		);
		this.initForm(this.formInstance);
		this.initTable(this.tableInstance);
	}

	/** 刷新 */
	refresh(url?: string) {
		const { page, pageSize } = this.pageInfo;
		this.requestPageData(page, pageSize, url);
	}

	setTableSource(data: Table[]): void {
		this.tableInstance.setTableSource(data);
	}
	getTableSource(): Table[] {
		return this.tableInstance.getTableSource();
	}
	addTableCol(col: TableColsOptions<keyof Table> & TableColsOptions<keyof Table>[]): void {
		this.tableInstance.addTableCol(col);
	}
	removeTableCol(key: keyof Table): void {
		this.tableInstance.removeTableCol(key);
	}
	getTableColsConf(): TableColsOptions<keyof Table>[] {
		return this.tableInstance.getTableColsConf();
	}
	addTableOpr(opr: TableOperateType<Table>): void {
		this.tableInstance.addTableOpr(opr);
	}
	removeTableOpr(label: string): void {
		this.tableInstance.removeTableOpr(label);
	}
	setTableOprDisabled(label: string, disabled: boolean): void {
		this.tableInstance.setTableOprDisabled(label, disabled);
	}
	getTableOprConf(): TableOperateType<Table>[] {
		return this.tableInstance.getTableOprConf();
	}
	setTableSelection(state: boolean): void {
		this.tableInstance.setTableSelection(state);
	}
	setChildTable(options: LoadChildTableOptions<Table>) {
		this.tableInstance.setChildTable(options);
	}
	setTotal(total: number): void {
		this.tableInstance.setTotal(total);
	}
	setTableIndex(state: boolean): void {
		this.tableInstance.setTableIndex(state);
	}
	setTableSelectionData(rows: Array<Table>): void {
		this.tableInstance.setTableSelectionData(rows);
	}
	getTableSelectionData(): Table[] {
		return this.tableInstance.getTableSelectionData();
	}
	addFormItem(opts: FormItemOptions<Form>): FormItem<Form> {
		const item = this.formInstance.addFormItem(opts);
		return item;
	}

	removeFormItem(key: keyof Form): void {
		this.formInstance.removeFormItem(key);
	}

	getFormItemValue(key: keyof Form): FormItem<Form> {
		const item = this.formInstance.getFormItemValue(key);
		return item;
	}
	setFormItemValue(key: keyof Form, value: Form[keyof Form]): void {
		this.formInstance.setFormItemValue(key, value);
		this.repaint();
	}
	getFormValue(): Form {
		return this.formInstance.getFormValue();
	}
	setFormValue(form: Form): void {
		this.formInstance.setFormValue(form);
	}
	setFormItemDisabled(key: string, state: boolean): void {
		this.formInstance.setFormItemDisabled(key, state);
	}
	setFormDisabled(state: boolean): void {
		this.formInstance.setFormDisabled(state);
	}
	getFormConf(): FormItemOptions<Form>[] {
		return this.formInstance.getFormConf();
	}
	setFormConf(key: keyof Form, opt: FormItemComponentType): void {
		return this.formInstance.setFormItemConf(key, opt);
	}
	reset(): void {
		this.formInstance.reset();
	}
	async verify(): Promise<boolean> {
		return await this.formInstance.verify();
	}

	addFormButton(opts: ButtonItemOptions): ButtonItem {
		const btnItem = this.formInstance.addFormButton(opts);
		return btnItem;
	}
	removeFormButton(key: string): void {
		this.formInstance.removeFormButton(key);
	}
	setFormButtonDisabled(key: string, state: boolean): void {
		this.formInstance.setFormButtonDisabled(key, state);
	}

	getLayout(): VNode {
		const formSlot = Object.keys(this.formInstance.getFormValue() as Object).length
			? () => this.formInstance.getVNode()
			: undefined;
		const tableSlot = () => this.tableInstance.getVNode();
		return createVNode(
			DataQueryLayoutVue,
			{ id: "test-test" },
			{
				form: formSlot,
				table: tableSlot,
			}
		);
	}
	getVNode() {
		// 布局的VNode
		return this.createPageComponent();
	}

	async requestPageData(
		page: number = this.pageInfo.page,
		pageSize: number = this.pageInfo.pageSize,
		url?: string
	) {
		let curUrl = url || this.url;
		const {
			method = "GET",
			headerTyps = HeadersType.Urlencoded,
			resultField = "items",
			mergeParams = true,
			searchFilter,
			filterData,
			otherCodeReplace,
		} = this.option;
		const formValue = this.getFormValue();
		/** 替换 page 和 pageSize 之外的参数 */
		const expand = otherCodeReplace && otherCodeReplace(formValue);
		if (expand) {
			Object.keys(expand).forEach(key => (curUrl = signReplace(curUrl, key, expand[key])));
		}
		/** 刷新操作 可能为新的 url */
		curUrl = signReplace(curUrl, "page", page.toString());
		curUrl = signReplace(curUrl, "pageSize", pageSize.toString());
		this.tableInstance.showLoading();

		let res!: HttpResult<IResult<Table>>;
		try {
			const params = searchFilter ? searchFilter(formValue) : formValue;
			res = await new HttpRequest<HttpResult<IResult<Table>>>(curUrl, { header: headerTyps })[
				method.toLowerCase() as Lowercase<Methods>
			]({
				...(mergeParams ? { ...formValue, ...params } : params),
				...{ page, pageSize },
				...{ page: page, pageSize },
			});
		} catch (error) {
			console.error(error);
			this.tableInstance.hideLoading();
		}
		this.tableInstance.hideLoading();
		if (res.code == 200) {
			const newRows = filterData
				? filterData(res.result && res.result[resultField]!)
				: res.result && res.result[resultField];
			this.setTableSource(newRows || []);
			if (res.result && res.result.total == undefined) {
				this.tableInstance.setDispalyPaginator(false);
			} else {
				this.setTotal(Number(res.result && res.result.total));
			}
		}
	}

	abstract initForm(form: BasicFormBox<Form>): void;
	abstract initTable(table: BasicTaleBox<Table>): void;
}
